﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using Spencen.Mobile.UI.Easings;
using System.Drawing;

namespace Spencen.Mobile.UI.Transitions
{
    public abstract class Transition
    {
        public DrawingElement OldView { get; set; }
        public DrawingElement NewView { get; set; }
        public TimeSpan Duration { get; set; }
        public IEasingFunction EasingFunction { get; set; }

        private Color BackColor { get; set; }

        public Transition()
        {
            BackColor = Color.White;
        }

        public void Start( GraphicsContext graphicsContext )
        {
            if ( graphicsContext == null ) throw new ArgumentNullException( "graphicsContext" );
            if ( OldView == null || NewView == null )
                throw new InvalidOperationException( "Must set the OldView and NewView properties before starting the transition." );

            // Create an offscreen bitmap for the composite of the two views as they animate.
            var compositeBitmap = new Bitmap( OldView.Size.Width, OldView.Size.Height );

            // Create an offscreen bitmap to render the new view to.
            var newViewBitmap = new Bitmap( NewView.Size.Width, NewView.Size.Height );
            var newViewGraphics = Graphics.FromImage( newViewBitmap );
            newViewGraphics.Clear( BackColor );

            // Render the new view so to the offscreen bitmap so we can copy to the new position each frame
            var newContext = new GraphicsContext( newViewGraphics, NewView );
            NewView.Render( newContext );

            // Create an offscreen bitmap to render the old view to.
            var oldViewBitmap = new Bitmap( OldView.Size.Width, OldView.Size.Height );
            var oldViewGraphics = Graphics.FromImage( oldViewBitmap );
            oldViewGraphics.Clear( BackColor );

            // Render the new view so to the offscreen bitmap so we can copy to the new position each frame
            var oldContext = new GraphicsContext( oldViewGraphics, OldView );
            OldView.Render( oldContext );

            var canvasGraphics = graphicsContext.Graphics;
            using ( var compositeGraphics = Graphics.FromImage( compositeBitmap ) )
            {
                bool done = false;
                int frame = 0;
                long startTick = Environment.TickCount;

                Point startOffset = Point.Empty;
                Brush backBrush = OldView.Background; // new SolidBrush( BackColor );

                while ( !done )
                {
                    frame++;
                    float percentageComplete = Math.Min( ( Environment.TickCount - startTick ) / (float)( Duration.Ticks / 10000 ), 1.0f ); // Divide by 10000 to convert DateTime Ticks (100 nano-seconds) to milliseconds
                    done = percentageComplete == 1.0f;
                    float adjustedValue;

                    if ( EasingFunction == null )
                        adjustedValue = percentageComplete;
                    else
                        adjustedValue = EasingFunction.EasedValue( percentageComplete );

                    RenderFrame( compositeGraphics, compositeBitmap, newViewBitmap, oldViewBitmap, startOffset, backBrush, adjustedValue );

                    // Render the composite to the on-screen surface.
                    canvasGraphics.DrawImage( compositeBitmap, 0, 0 );
                }
                backBrush.Dispose();
            }

            oldViewGraphics.Dispose();
            oldViewBitmap.Dispose(); 

            newViewGraphics.Dispose();
            newViewBitmap.Dispose();

            compositeBitmap.Dispose();
        }

        protected abstract void RenderFrame( Graphics compositeGraphics, Bitmap compositeBitmap, Bitmap newViewBitmap, Bitmap oldViewBitmap, Point startOffset, Brush backBrush, float adjustedValue );
    }
}
